class GameEngineSystem {
    onFrame(advancedTime) {

    }
    onRecieveEvent(eventName: string) {

    }
}

class GameEngineMouseListenerSystem extends GameEngineSystem {
    currentEvent = null;

    constructor() {
        super();
        window.addEventListener('click', (e) => {
            this.currentEvent = e;
        });
    }
    onClick(e) {
        const mouseGlobalX = e.offsetX;
        const mouseGlobalY = e.offsetY;
        const point = { x: mouseGlobalX, y: mouseGlobalY };
        let result = this.hitTest(core.root, point);
        while (result && result.visible) {
            if (result.onClick) {
                result.onClick();
            }
            result = result.parent;
        }
    }

    hitTest(gameObject, point) {
        if (gameObject.renderer) {
            return gameObject.renderer.hitTest(point);
        }
        for (let i = gameObject.children.length - 1; i >= 0; i--) {
            const child = gameObject.children[i];
            const childTransform = child.getBehaviour(Transform);
            const localMatrix = childTransform.localMatrix;
            const invertLocalMatrix = invertMatrix(localMatrix);
            const childLocalPoint = pointAppendMatrix(point, invertLocalMatrix);
            const result = this.hitTest(child, childLocalPoint);
            if (result) {
                return result;
            }
        }
        return null;

    }

    onFrame(advancedTime) {
        if (this.currentEvent) {
            this.onClick(this.currentEvent);
            this.currentEvent = null;
        }
    }
}

class GameEngineRenderSystem extends GameEngineSystem {
    onFrame(advancedTime) {
        this.gameEngineDraw(core.root);
    }

    gameEngineDraw(gameObject) {
        const transform = gameObject.getBehaviour(Transform);
        transform.calculateGlobalProperties();
        context.setTransform(
            transform.globalMatrix.a,
            transform.globalMatrix.b,
            transform.globalMatrix.c,
            transform.globalMatrix.d,
            transform.globalMatrix.tx,
            transform.globalMatrix.ty
        )
        context.globalAlpha = transform.globalAlpha;

        if (gameObject.visible) {
            if (gameObject.renderer) {
                gameObject.renderer.onDraw(context);
            }
            for (let child of gameObject.children) {
                this.gameEngineDraw(child);
            }
        }
    }
}

class GameEnginePlayModeLifeCycleSystem extends GameEngineSystem {
    lastIterationTime = 0;

    lastAdvancedTime = 0;

    onFrame(advancedTime: number) {
        const duringTime = advancedTime - this.lastAdvancedTime;
        this.lastAdvancedTime = advancedTime;
        this.lastIterationTime += duringTime;
        const frameTime = 1000 / 60;
        while (this.lastIterationTime >= frameTime) {
            this.lastIterationTime -= frameTime;
            this.gameObjectOnStart(core.root);
            this.gameObjectOnUpdate(core.root, duringTime);
            this.gameObjectOnDestroy(core.root);
        }
    }

    gameObjectOnStart(gameObject: GameObject) {
        for (const behaviour of gameObject.behaviours) {
            if (!behaviour.isExecuteOnStart) {
                behaviour.onStart();
                behaviour.isExecuteOnStart = true;
            }
            for (let child of gameObject.children) {
                this.gameObjectOnStart(child);
            }
        }
    }

    gameObjectOnUpdate(gameObject: GameObject, duringTime: number) {
        const newBeahviour = [];
        for(let behaviour of gameObject.behaviours){
            newBeahviour.push(behaviour);
        }
        for (const behaviour of newBeahviour) {
            behaviour.onUpdate(duringTime);
        }
        for (let child of gameObject.children) {
            this.gameObjectOnUpdate(child, duringTime);
        }
    }

    gameObjectOnDestroy(gameObject: GameObject) {
        const newBehaviours = [];
        for (const behaviour of gameObject.behaviours) {
            if (behaviour.isDestroy) {
                behaviour.onDestroy();
            } else {
                newBehaviours.push(behaviour);
            }
        }
        gameObject.behaviours = newBehaviours;
        for (let child of gameObject.children) {
            this.gameObjectOnDestroy(child);
        }
    }

}

class GameEngineEditorModeDestoryLifeCycleSystem extends GameEngineSystem {
    lastIterationTime = 0;

    lastAdvancedTime = 0;

    onFrame(advancedTime: number) {
        const duringTime = advancedTime - this.lastAdvancedTime;
        this.lastAdvancedTime = advancedTime;
        this.lastIterationTime += duringTime;
        const frameTime = 1000 / 60;
        while (this.lastIterationTime >= frameTime) {
            this.lastIterationTime -= frameTime;
            this.gameObjectOnDestroy(core.root);
        }
    }

    gameObjectOnDestroy(gameObject: GameObject) {
        const newBehaviours: Behaviour[] = [];
        for (const behaviour of gameObject.behaviours) {
            if (behaviour.isDestroy) {
                behaviour.onDestroy();
            } else {
                newBehaviours.push(behaviour);
            }
        }
        gameObject.behaviours = newBehaviours;
        for (let child of gameObject.children) {
            this.gameObjectOnDestroy(child);
        }
    }

}

class EditorApiSystem extends GameEngineSystem {
    private isFrist: boolean = true;

    private currentSceneUrl = "data.json";
    onFrame() {
        if (this.isFrist) {
            this.isFrist = false;
            nativeMune.run(this);
            hierarchyPanel.run(this);
            inspectorPanel.run(this);
            resourcePanel.run(this);
        }
    }

    onRecieveEvent(eventName: string) {
        if (eventName = "sceneChanged") {
            context.clearRect(0,0,canvas.width,canvas.height);
            const hierarchydata = getHierarchyData(core.root);
            hierarchyPanel.update(hierarchydata);
            inspectorPanel.clear();
        }
    }

    executeCommand(name: string, param?: any) {
        console.log("执行命令", name, "参数", param);
        switch (name) {
            case "SelectGameObject":
                const selectedGameObject = GameObject.getGameObject(param);
                const gameObjectData = getInspectorData(selectedGameObject);
                inspectorPanel.update(gameObjectData);
                break;
            case "ModifyBehaviourProperty":
                const modifiedBehaviour = Behaviour.getBehaviour(param.uuid);
                modifiedBehaviour[param.key] = param.newValue;
                break;
            case "Save":
                const data = core.toData(core.root.children[0]);
                const content = JSON.stringify(data, null, "\t");
                const fs = require("fs");
                fs.writeFileSync(this.currentSceneUrl, content, "utf-8");
                core.loadAllResource(()=>{
                    
                });
                break;
            case "ChangeScene":
                this.currentSceneUrl = param;
                core.changeScene(this.currentSceneUrl);
                break;
            case "AddBehaviourOnGameObject":
                const addGameObjectUuid = param.uuid;
                const addBehaviourName = param.behaviourName;
                const addGameObject = GameObject.getGameObject(addGameObjectUuid);
                const behaviourClass = core.getBehaviourClass(addBehaviourName);
                const addBehaviour = new behaviourClass();
                const properites = addBehaviour["constructor"]["properties"];
                if (addGameObject.getBehaviour(behaviourClass)) {
                    console.log("已经有behaviour:", behaviourClass.name, "，不能再添加这个behaviour");
                    const alertMessage = "已经有behaviour:" + behaviourClass.name + "不能再添加这个behaviour";
                    alert(alertMessage);
                    return;
                } else if (addGameObject.renderer) {
                    for (let render of Render.getAllRender()) {
                        if (render == addBehaviour) {
                            console.log("已经有render，无法再添加render");
                            alert("已经有render，无法再添加render");
                            return;
                        }
                    }
                }
                if (properites) {
                    for (let p of properites) {
                        const key = p.key;
                        const defaultValue = p.defaultValue;
                        if (defaultValue) {
                            addBehaviour[key] = defaultValue;
                        } else {
                            addBehaviour[key] = "";
                        }
                    }
                }
                addGameObject.addBehaviour(addBehaviour);
                inspectorPanel.update(getInspectorData(addGameObject));
                break;
            case "DeleteBehaviourFormGameObject":
                const deleteBehaviourGameObjectUuid = param.uuid;
                const deleteBehaviourName = param.behaviourName;
                const deleteBehaviourGameObject = GameObject.getGameObject(deleteBehaviourGameObjectUuid);
                const deleteBehaviourClass = core.getBehaviourClass(deleteBehaviourName);
                const deleteBehaviour = deleteBehaviourGameObject.getBehaviour(deleteBehaviourClass);
                deleteBehaviourGameObject.removeBehaviour(deleteBehaviour);
                inspectorPanel.update(getInspectorData(deleteBehaviourGameObject));
                console.log("删除gameObject：", deleteBehaviourGameObject, "上的behaviour：", deleteBehaviour);
                break;
            case "AddEmptyGameObjectChildToGameObject":
                const addChildGameObjectUuid =param.uuid;
                const addChildGameObject = GameObject.getGameObject(addChildGameObjectUuid);
                const childGameObject = new GameObject();
                const newTransform = new Transform();
                const transformProperties = newTransform["constructor"]["properties"];
                if (transformProperties) {
                    for (let p of transformProperties) {
                        const key = p.key;
                        const defaultValue = p.defaultValue;
                        if (defaultValue) {
                            newTransform[key] = defaultValue;
                        } else {
                            newTransform[key] = 0;
                        }
                    }
                }
                childGameObject.addBehaviour(newTransform);
                console.log(addChildGameObject)
                console.log(childGameObject)
                addChildGameObject.addChild(childGameObject);
                const hierarchydata = getHierarchyData(core.root);
                hierarchyPanel.update(hierarchydata);
                break;
            case "DeleteGameObject":
                const deleteGameObjectUuid = param.uuid;
                const deleteGameObject = GameObject.getGameObject(deleteGameObjectUuid);
                deleteGameObject.parent.removeChild(deleteGameObject);
                const hierarchydata1 = getHierarchyData(core.root);
                hierarchyPanel.update(hierarchydata1);
                break;
        }

    }
}